home *** CD-ROM | disk | FTP | other *** search
- /* OS- and machine-dependent stuff for the 8250 asynch chip on a IBM-PC
- *
- * 16550A support plus some statistics added mah@hpuviea.at 15/7/89
- *
- * CTS hardware flow control from dkstevens@ucdavis,
- * additional stats from delaroca@oac.ucla.edu added by karn 4/17/90
- * 8250 support for above by PA0GRI (hardware kick, interrupt does not come up)
- */
- #include <stdio.h>
- #include <dos.h>
- #include "global.h"
- #include "config.h"
- #ifdef ASY
- #include "mbuf.h"
- #include "proc.h"
- #include "iface.h"
- #include "asy.h"
- #include "pc.h"
- #include "slip.h"
- #include "8250.h"
- #ifdef NRS
- #include "nrs.h"
- #endif
-
- static int near asyrxint __ARGS((struct asy *asyp));
- static void near asytxint __ARGS((int dev));
- static void near asymsint __ARGS((int dev));
-
- struct asy Asy[ASY_MAX];
- /* ASY interrupt handlers */
- static INTERRUPT (*Handle[])() = {asy0vec,asy1vec,asy2vec,asy3vec,asy4vec};
-
- /* Initialize asynch port "dev" */
- int
- asy_init(dev,iface,arg1,arg2,bufsize,trigchar,cts)
- int dev;
- struct iface *iface;
- char *arg1,*arg2; /* Attach args for address and vector */
- unsigned bufsize;
- int trigchar;
- char cts;
- {
- struct fifo *fp;
- int i_state;
- struct asy *ap = &Asy[dev];
- unsigned base;
-
- if(bufsize) {
- ap->iface = iface;
- ap->addr = htoi(arg1);
- ap->vec = htoi(arg2);
- ap->cts_flow_control = cts;
- ap->trigchar = trigchar;
-
- /* Set up receiver FIFO */
- fp = &ap->fifo;
- fp->buf = mxallocw(bufsize);
- fp->bufsize = bufsize;
- fp->wp = fp->rp = fp->buf;
- fp->cnt = fp->hiwat = fp->overrun = 0;
- }
-
- base = ap->addr;
- /* Purge the receive data buffer */
- (void)inportb(base+RBR);
-
- i_state = dirps();
-
- /* Save original interrupt vector, mask state, control bits */
- ap->save.vec = getirq(ap->vec);
- ap->save.mask = getmask(ap->vec);
- ap->save.lcr = inportb(base+LCR);
- ap->save.ier = inportb(base+IER);
- ap->save.mcr = inportb(base+MCR);
-
- /* save speed bytes */
- setbit(base+LCR,LCR_DLAB);
- ap->save.divl = inportb(base+DLL);
- ap->save.divh = inportb(base+DLM);
- clrbit(base+LCR,LCR_DLAB);
-
- /* Set interrupt vector to SIO handler */
- setirq(ap->vec,Handle[dev]);
-
- /* Set line control register: 8 bits, no parity */
- outportb(base+LCR,(char)LCR_8BITS);
-
- /* determine if 16550A, turn on FIFO mode and clear RX and TX FIFOs */
- outportb(base+FCR,(char) FIFO_ENABLE);
-
- /* According to National ap note AN-493, the FIFO in the 16550 chip
- * is broken and must not be used. To determine if this is a 16550A
- * (which has a good FIFO implementation) check that both bits 7
- * and 6 of the IIR are 1 after setting the fifo enable bit. If
- * not, don't try to use the FIFO.
- */
- if ((inportb(base+IIR) & IIR_FIFO_ENABLED) == IIR_FIFO_ENABLED) {
- ap->is_16550a = 1;
- outportb(base+FCR,(char) FIFO_SETUP);
- } else {
- /* Chip is not a 16550A. In case it's a 16550 (which has a
- * broken FIFO), turn off the FIFO bit.
- */
- outportb(base+FCR,(char)0);
- ap->is_16550a = 0;
- }
- /* Turn on receive interrupt enable in 8250, leave transmit
- * and modem status interrupts turned off for now
- */
- outportb(base+IER,(char)IER_DAV);
-
- /* Set modem control register: assert DTR, RTS, turn on 8250
- * master interrupt enable (connected to OUT2)
- */
- outportb(base+MCR,(char)(MCR_DTR|MCR_RTS|MCR_OUT2));
-
- /* Enable interrupt */
- maskon(ap->vec);
- restore(i_state);
- return 0;
- }
-
- int
- asy_stop(iface,tmp)
- struct iface *iface;
- int tmp;
- {
- int i_state;
- struct asy *ap = &Asy[iface->dev];
- unsigned base;
-
- /*-------------------------------------------------------------------*
- * A few notes to the delay below: *
- * It sometimes happened, that NOS simply hangs when an exit is issued*
- * and more than one remote session is active. *
- * the reset_all() in between doexit() resets all the remote connec- *
- * tions causing a RST packet being sent to all those remote instances*
- * During this work, NOS tries to detach the interfaces and in case *
- * a 16650A is involved, the hardware FIFO is reset and POOF! *
- * The delay gives enough time to send the packets (I hope) DK5DC *
- *--------------------------------------------------------------------*/
- #ifdef MSDOS
- pause(1000); /* let the chip send all data */
- #endif
-
- if(!tmp) {
- ap->iface = NULLIF;
- /* Release slip or nrs block */
- #ifdef SLIP
- if(Slip[iface->xdev].iface == iface)
- Slip[iface->xdev].iface = NULLIF;
- #endif
- #ifdef NRS
- if(Nrs[iface->xdev].iface == iface)
- Nrs[iface->xdev].iface = NULLIF;
- #endif
- }
-
- base = ap->addr;
- /* Purge the receive data buffer */
- (void)inportb(base+RBR);
-
- /* and hardware fifos if available */
- if (ap->is_16550a)
- outportb(base+FCR,(char) FIFO_SETUP);
-
- /* Restore original interrupt vector and 8259 mask state */
- i_state = dirps();
- setirq(ap->vec,ap->save.vec);
- if (ap->save.mask)
- maskon(ap->vec);
- else
- maskoff(ap->vec);
-
- /* Restore speed regs */
- setbit(base+LCR,LCR_DLAB);
- outportb(base+DLL,ap->save.divl); /* Low byte */
- outportb(base+DLM,ap->save.divh); /* Hi byte */
- clrbit(base+LCR,LCR_DLAB);
-
- /* Restore control regs */
- outportb(base+LCR,ap->save.lcr);
- outportb(base+IER,ap->save.ier);
- outportb(base+MCR,ap->save.mcr);
- restore(i_state);
- if(!tmp)
- xfree(ap->fifo.buf);
- return 0;
- }
-
- /* Asynchronous line I/O control */
- int
- asy_ioctl(iface,argc,argv)
- struct iface *iface;
- int argc;
- char *argv[];
- {
- if(argc < 1){
- tprintf("%u\n",Asy[iface->dev].speed);
- return 0;
- }
- return asy_speed(iface->dev,atoi(argv[0]));
- }
-
- /* Set asynch line speed */
- int
- asy_speed(dev,speed)
- int dev;
- long speed;
- {
- int i_state;
- unsigned base = Asy[dev].addr;
- int32 divisor = BAUDCLK / speed;
-
- if(speed == 0 || dev >= ASY_MAX)
- return -1;
-
- Asy[dev].speed = speed;
-
- i_state = dirps();
-
- /* Purge the receive data buffer */
- (void)inportb(base+RBR);
- if (Asy[dev].is_16550a) /* clear tx+rx fifos */
- outportb(base+FCR,(char) FIFO_SETUP);
-
- /* Turn on divisor latch access bit */
- setbit(base+LCR,LCR_DLAB);
-
- /* Load the two bytes of the register */
- outportb(base+DLL,(char)(divisor & 0xff)); /* Low byte */
- outportb(base+DLM,(char)((divisor >> 8) & 0xff)); /* Hi byte */
-
- /* Turn off divisor latch access bit */
- clrbit(base+LCR,LCR_DLAB);
-
- restore(i_state);
- return 0;
- }
-
- /* Start transmission of a buffer on the serial transmitter */
- static void near
- asy_output(int dev,char *buf,unsigned short cnt)
- {
- int i_state, ier;
- struct asy *asyp = &Asy[dev];
- unsigned base = asyp->addr;
- struct dma *dp = &asyp->dma;
-
- if(dev < 0 || dev >= ASY_MAX || asyp->iface == NULLIF)
- return;
-
- i_state = dirps();
-
- if(dp->flags){
- restore(i_state);
- return; /* Already busy */
- }
- dp->data = buf;
- dp->cnt = cnt;
- dp->flags = 1;
-
- if(asyp->cts_flow_control){
- /* CTS flow control is enabled; let the modem control
- * interrupt enable transmit interrupts if CTS is off
- */
- ier = IER_MS;
- if(inportb(base+MSR) & MSR_CTS)
- ier |= IER_TxE;
- } else {
- /* Enable transmit interrupts; this will cause an immediate
- * interrupt that will start things going
- */
- ier = IER_TxE;
- }
- setbit(base+IER,ier);
- /* "Kick start" the transmitter interrupt routine, in case just
- * setting the interrupt enable bit doesn't case an interrupt
- */
- if(ier & IER_TxE)
- asytxint(dev);
- restore(i_state);
- }
-
- /* Blocking read from asynch line
- * Returns count of characters read
- */
- int
- get_asy(dev)
- int dev;
- {
- struct fifo *fp = &Asy[dev].fifo;
- int c, i_state = dirps();
-
- while(fp->cnt == 0){
- if(pwait(fp) != 0){
- restore(i_state);
- return -1;
- }
- }
- fp->cnt--;
- restore(i_state);
-
- c = *fp->rp++;
-
- if(fp->rp >= &fp->buf[fp->bufsize])
- fp->rp = fp->buf;
-
- return c;
- }
-
- /* Interrupt handler for 8250 asynch chip */
- void
- asyint(dev)
- int dev;
- {
- char iir;
- struct asy *asyp = &Asy[dev];
- unsigned base = asyp->addr;
-
- while(((iir = inportb(base+IIR)) & IIR_IP) == 0){
- switch(iir & IIR_ID){
- case IIR_RDA: /* Receiver interrupt */
- asyrxint(asyp);
- break;
- case IIR_THRE: /* Transmit interrupt */
- asytxint(dev);
- break;
- case IIR_MSTAT: /* Modem status change */
- asymsint(dev);
- asyp->cts_flow_ints++;
- break;
- }
- /* should happen at end of a single slip packet */
- if(iir & IIR_FIFO_TIMEOUT)
- asyp->fifotimeouts++;
- }
- }
-
- /* Process 8250 receiver interrupts */
- static int near
- asyrxint(asyp)
- struct asy *asyp;
- {
- int c, lsr, cnt = 0, trigseen = 0;
-
- unsigned base = asyp->addr;
- struct fifo *fp = &asyp->fifo;
-
- asyp->rxints++;
-
- for(;;){
- if((lsr = inportb(base+LSR)) & LSR_OE)
- asyp->overrun++;
-
- if(lsr & LSR_DR){
- asyp->rxchar++;
-
- if((c = inportb(base+RBR)) == asyp->trigchar)
- trigseen = 1;
-
- /* If buffer is full, we have no choice but
- * to drop the character
- */
- if(fp->cnt != fp->bufsize){
- *fp->wp++ = c;
- if(fp->wp >= &fp->buf[fp->bufsize])
- /* Wrap around */
- fp->wp = fp->buf;
-
- if(++fp->cnt > fp->hiwat)
- fp->hiwat = fp->cnt;
-
- cnt++;
- } else
- fp->overrun++;
- } else
- break;
- }
- if(cnt > asyp->rxhiwat)
- asyp->rxhiwat = cnt;
- if(trigseen)
- psignal(fp,1);
- return cnt;
- }
-
- /* Handle 8250 transmitter interrupts */
- static void near
- asytxint(dev)
- int dev;
- {
- int count;
- struct asy *asyp = &Asy[dev];
- unsigned base = asyp->addr;
- struct dma *dp = &asyp->dma;
-
- asyp->txints++;
-
- if(!dp->flags){
- /* "Shouldn't happen", but disable transmit
- * interrupts anyway
- */
- clrbit(base+IER,IER_TxE);
- return; /* Nothing to send */
- }
- if(!(inportb(base+LSR) & LSR_THRE))
- return; /* Not really ready */
-
- /* If it's a 16550A, load up to 16 chars into the tx hw fifo
- * at once. With an 8250, it can be one char at most.
- */
- if(asyp->is_16550a){
- count = (int)min(dp->cnt,OUTPUT_FIFO_SIZE);
-
- /* 16550A: LSR_THRE will drop after the first char loaded
- * so we can't look at this bit to determine if the hw fifo is
- * full. There seems to be no way to determine if the tx fifo
- * is full (any clues?). So we should never get here while the
- * fifo isn't empty yet.
- */
- asyp->txchar += count;
- dp->cnt -= count;
- #ifdef XXX /* This is apparently too fast for some chips */
- dp->data = outbuf(base+THR,dp->data,count);
- #else
- while(count-- != 0)
- outportb(base+THR,*dp->data++);
- #endif
- if(dp->cnt == 0){
- dp->flags = 0;
- /* Disable transmit interrupts */
- clrbit(base+IER,IER_TxE);
- psignal(asyp,1);
- }
- } else { /* 8250 */
- do {
- asyp->txchar++;
- outportb(base+THR,*dp->data++);
-
- if(--dp->cnt == 0){
- dp->flags = 0;
- /* Disable transmit interrupts */
- clrbit(base+IER,IER_TxE);
- psignal(asyp,1);
- break;
- }
- } while(inportb(base+LSR) & LSR_THRE);
- }
- }
-
- /* Handle 8250 modem status change */
- static void near
- asymsint(dev)
- int dev;
- {
- struct asy *asyp = &Asy[dev];
- unsigned base = asyp->addr;
- struct dma *dp = &asyp->dma;
- char msr = inportb(base+MSR);
-
- if (asyp->cts_flow_control) {
- if(msr & MSR_CTS){
- /* CTS now asserted, enable Transmit interrupts */
- if(dp->flags) {
- setbit(base+IER,IER_TxE);
- /* asytxint(dev); */
- }
- } else {
- /* CTS now dropped, disable Transmit interrupts */
- clrbit(base+IER,IER_TxE);
- }
- }
- }
-
- /* Poll the asynch input queues; called on every clock tick.
- * This helps limit the interrupt ring buffer occupancy when long
- * packets are being received.
- */
- void
- asytimer()
- {
- int i;
- struct asy *asyp;
- struct fifo *fp;
-
- for(i = 0; i < ASY_MAX; i++) {
- asyp = &Asy[i];
- fp = &asyp->fifo;
-
- if(fp->cnt != 0)
- psignal(fp,1);
- if(asyp->dma.flags != 0 && (inportb(asyp->addr+LSR) & LSR_THRE) ) {
- asyp->txto++;
- asytxint(asyp->iface->dev);
- }
- }
- }
-
- int
- doasystat(argc,argv,p)
- int argc;
- char *argv[];
- void *p;
- {
- struct asy *asyp;
-
- for(asyp = Asy;asyp < &Asy[ASY_MAX];asyp++){
- if(asyp->iface == NULLIF)
- continue;
-
- tprintf("%s:",asyp->iface->name);
- if(asyp->is_16550a)
- tputs(" [NS16550A]");
- if(asyp->trigchar != -1)
- tprintf(" [trigger 0x%02x]",asyp->trigchar);
- if(asyp->cts_flow_control)
- tputs(" [cts flow control]");
- tprintf(" %u bps\n",asyp->speed);
- tprintf(" RX: int %lu chr %lu hwovrn %lu hwhiwat %lu",
- asyp->rxints,
- asyp->rxchar,
- asyp->overrun,
- asyp->rxhiwat);
- asyp->rxhiwat = 0;
- if(asyp->is_16550a)
- tprintf(" fifoTO %lu",asyp->fifotimeouts);
- tprintf(" swovrn %lu swhiwat %u\n",
- asyp->fifo.overrun,
- asyp->fifo.hiwat);
- asyp->fifo.hiwat = 0;
- tprintf(" TX: int %lu chr %lu sndq %u CTS/RLSD %lu int %lu\n",
- asyp->txints,
- asyp->txchar,
- len_q(asyp->sndq),
- asyp->cts_flow_ints,
- asyp->txto);
- }
- return 0;
- }
-
- /* Send a message on the specified serial line */
- int
- asy_send(dev,bp)
- int dev;
- struct mbuf *bp;
- {
- if(dev < 0 || dev >= ASY_MAX)
- return -1;
-
- enqueue(&Asy[dev].sndq,bp);
- return 0;
- }
-
- /* Serial transmit process, common to all protocols */
- void
- asy_tx(dev,p1,p2)
- int dev;
- void *p1;
- void *p2;
- {
- struct mbuf *bp;
- int i_state;
- struct asy *asyp = &Asy[dev];
- struct dma *dp = &asyp->dma;
-
- for(;;){
- /* Fetch a buffer for transmission */
- while(asyp->sndq == NULLBUF)
- pwait(&asyp->sndq);
- bp = dequeue(&asyp->sndq);
-
- while(bp != NULLBUF){
- /* Start the transmitter */
- asy_output(dev,bp->data,bp->cnt);
-
- /* Wait for completion */
- i_state = dirps();
- while(dp->flags == 1)
- pwait(asyp);
- restore(i_state);
-
- /* Now do next buffer on chain */
- bp = free_mbuf(bp);
- }
- }
- }
-
- #endif /* ASY */